﻿using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using NHotkey;
using NHotkey.Wpf;

namespace GameSaveManager;

public class GameProfile
{
    public string Name { get; set; } = string.Empty;
    public string SourcePath { get; set; } = string.Empty;
    public bool IsAutoBackupEnabled { get; set; } = false;
    public int AutoBackupIntervalMinutes { get; set; } = 10;
    public int MaxBackups { get; set; } = 10;
}

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    private const string ProfilesFilePath = "GameProfiles.json";
    private List<GameProfile> gameProfiles = new List<GameProfile>();
    private Dictionary<string, System.Threading.Timer> autoBackupTimers = new Dictionary<string, System.Threading.Timer>();
    private AppSettings appSettings = null!;

    public MainWindow()
    {
        InitializeComponent();
        LoadProfiles();
        GameProfilesListBox.ItemsSource = gameProfiles;
        GameProfilesListBox.DisplayMemberPath = "Name";

        // Start auto-backups for enabled games
        foreach (var profile in gameProfiles)
        {
            if (profile.IsAutoBackupEnabled)
            {
                StartAutoBackup(profile);
            }
        }

        // Load settings and register global hotkeys
        RegisterHotkeys();
    }

    private void RegisterHotkeys()
    {
        try
        {
            // Load settings
            appSettings = SettingsManager.Load();

            // Register hotkeys using settings
            HotkeyManager.Current.AddOrReplace("BackupHotkey", appSettings.BackupHotkeyKey, appSettings.BackupHotkeyModifiers, BackupViaHotkey);
            HotkeyManager.Current.AddOrReplace("RestoreHotkey", appSettings.RestoreHotkeyKey, appSettings.RestoreHotkeyModifiers, RestoreViaHotkey);
        }
        catch (Exception ex)
        {
            StatusTextBlock.Text = $"Failed to register hotkeys: {ex.Message}";
        }
    }

    private void AddGame_Click(object sender, RoutedEventArgs e)
    {
        // Select Folder
        var dialog = new System.Windows.Forms.FolderBrowserDialog();
        var result = dialog.ShowDialog();

        if (result != System.Windows.Forms.DialogResult.OK)
        {
            return;
        }

        // Get Default Name
        string selectedPath = dialog.SelectedPath;
        string defaultName = System.IO.Path.GetFileName(selectedPath);

        // Show Input Dialog
        string gameName = Microsoft.VisualBasic.Interaction.InputBox(
            "Please enter a name for this save profile:",
            "Enter Game Name",
            defaultName);

        // Validate Input
        if (string.IsNullOrWhiteSpace(gameName))
        {
            return;
        }

        // Create and Add Profile
        var newProfile = new GameProfile
        {
            Name = gameName,
            SourcePath = selectedPath
        };

        gameProfiles.Add(newProfile);
        GameProfilesListBox.Items.Refresh();
        StatusTextBlock.Text = $"Game '{gameName}' has been added.";
        SaveProfiles();
    }

    private void DeleteGame_Click(object sender, RoutedEventArgs e)
    {
        var selectedProfile = GameProfilesListBox.SelectedItem as GameProfile;
        if (selectedProfile != null)
        {
            gameProfiles.Remove(selectedProfile);
            SaveProfiles();
            GameProfilesListBox.Items.Refresh();
            StatusTextBlock.Text = $"Game '{selectedProfile.Name}' has been deleted.";
        }
        else
        {
            StatusTextBlock.Text = "Please select a game to delete.";
        }
    }

    private void SettingsButton_Click(object sender, RoutedEventArgs e)
    {
        SettingsWindow settingsWindow = new SettingsWindow();
        bool? result = settingsWindow.ShowDialog(); // Use ShowDialog() to show it modally

        // Check if user clicked Save
        if (result == true)
        {
            try
            {
                // Unregister old hotkeys
                HotkeyManager.Current.Remove("BackupHotkey");
                HotkeyManager.Current.Remove("RestoreHotkey");

                // Reload settings and re-register hotkeys
                RegisterHotkeys();

                // Show confirmation message
                StatusTextBlock.Text = "Hotkey settings have been updated.";
            }
            catch (Exception ex)
            {
                StatusTextBlock.Text = $"Failed to update hotkeys: {ex.Message}";
            }
        }
    }

    private void BackupSelected_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            // Get selected game profile
            var selectedProfile = GameProfilesListBox.SelectedItem as GameProfile;

            // Validation: Check if a game is selected
            if (selectedProfile == null)
            {
                StatusTextBlock.Text = "Please select a game to back up first";
                return;
            }

            // Get Name and SourcePath
            string gameName = selectedProfile.Name;
            string sourcePath = selectedProfile.SourcePath;

            // Validate that SourcePath exists
            if (!System.IO.Directory.Exists(sourcePath))
            {
                StatusTextBlock.Text = $"Error: Save directory not found at '{sourcePath}'";
                return;
            }

            // Define Destination Path
            string timestamp = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
            string backupRootDir = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Backups");
            string gameBackupDir = System.IO.Path.Combine(backupRootDir, gameName);
            string destinationPath = System.IO.Path.Combine(gameBackupDir, timestamp);

            // Create directories if they don't exist
            if (!System.IO.Directory.Exists(gameBackupDir))
            {
                System.IO.Directory.CreateDirectory(gameBackupDir);
            }
            System.IO.Directory.CreateDirectory(destinationPath);

            // Execute Backup
            CopyDirectory(sourcePath, destinationPath);

            // Enforce backup limit
            EnforceBackupLimit(selectedProfile);

            // Refresh Backup List
            RefreshBackupsList(selectedProfile);

            // Provide Feedback
            StatusTextBlock.Text = $"Game '{gameName}' backed up successfully!";
            PlaySuccessSound();
        }
        catch (Exception ex)
        {
            StatusTextBlock.Text = $"Backup failed: {ex.Message}";
        }
    }

    private void RestoreSelectedBackup_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            // Get dual selections
            var selectedProfile = GameProfilesListBox.SelectedItem as GameProfile;
            var selectedTimestamp = BackupsListBox.SelectedItem as string;

            // Validate input
            if (selectedProfile == null || selectedTimestamp == null)
            {
                StatusTextBlock.Text = "Please select both a game and a backup version to restore";
                return;
            }

            // Construct paths
            string destinationPath = selectedProfile.SourcePath;
            string backupSourcePath = System.IO.Path.Combine(
                AppDomain.CurrentDomain.BaseDirectory,
                "Backups",
                selectedProfile.Name,
                selectedTimestamp);

            // Validate backup source exists
            if (!System.IO.Directory.Exists(backupSourcePath))
            {
                StatusTextBlock.Text = $"Error: Backup directory not found at '{backupSourcePath}'";
                return;
            }

            // Show confirmation dialog
            string warningMessage = $"WARNING: This will overwrite your current '{selectedProfile.Name}' saves with the backup from '{selectedTimestamp}'.\n\n" +
                                   "This action cannot be undone!\n\n" +
                                   "Are you sure you want to continue?";

            var result = System.Windows.MessageBox.Show(
                warningMessage,
                "Confirm Restore",
                MessageBoxButton.YesNo,
                MessageBoxImage.Warning);

            if (result != MessageBoxResult.Yes)
            {
                StatusTextBlock.Text = "Restore cancelled by user.";
                return;
            }

            // Clear destination directory
            if (System.IO.Directory.Exists(destinationPath))
            {
                // Delete all files in the destination
                foreach (var file in System.IO.Directory.GetFiles(destinationPath))
                {
                    System.IO.File.Delete(file);
                }

                // Delete all subdirectories in the destination
                foreach (var dir in System.IO.Directory.GetDirectories(destinationPath))
                {
                    System.IO.Directory.Delete(dir, true);
                }
            }
            else
            {
                // Create destination if it doesn't exist
                System.IO.Directory.CreateDirectory(destinationPath);
            }

            // Perform restore
            CopyDirectory(backupSourcePath, destinationPath);

            // Provide feedback
            StatusTextBlock.Text = $"'{selectedProfile.Name}' saves have been successfully restored from '{selectedTimestamp}'.";
        }
        catch (Exception ex)
        {
            StatusTextBlock.Text = $"Restore failed: {ex.Message}";
        }
    }

    private void GameProfilesListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var selectedProfile = GameProfilesListBox.SelectedItem as GameProfile;
        RefreshBackupsList(selectedProfile);
        UpdateAutoBackupControls(selectedProfile);
    }

    private void AutoBackupIntervalTextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        var selectedProfile = GameProfilesListBox.SelectedItem as GameProfile;
        if (selectedProfile == null)
        {
            return;
        }

        // Validate and parse the input
        if (int.TryParse(AutoBackupIntervalTextBox.Text, out int interval) && interval > 0)
        {
            selectedProfile.AutoBackupIntervalMinutes = interval;
            SaveProfiles();
        }
    }

    private void MaxBackupsTextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        var selectedProfile = GameProfilesListBox.SelectedItem as GameProfile;
        if (selectedProfile == null)
        {
            return;
        }

        // Validate and parse the input
        if (int.TryParse(MaxBackupsTextBox.Text, out int maxBackups) && maxBackups > 0)
        {
            selectedProfile.MaxBackups = maxBackups;
            SaveProfiles();
        }
    }

    private void UpdateAutoBackupControls(GameProfile? profile)
    {
        if (profile == null)
        {
            // Disable controls when no game is selected
            ToggleAutoBackupButton.IsEnabled = false;
            AutoBackupIntervalTextBox.IsEnabled = false;
            AutoBackupIntervalTextBox.Text = string.Empty;
            MaxBackupsTextBox.IsEnabled = false;
            MaxBackupsTextBox.Text = string.Empty;
        }
        else
        {
            // Enable controls when a game is selected
            ToggleAutoBackupButton.IsEnabled = true;
            AutoBackupIntervalTextBox.IsEnabled = true;
            AutoBackupIntervalTextBox.Text = profile.AutoBackupIntervalMinutes.ToString();
            MaxBackupsTextBox.IsEnabled = true;
            MaxBackupsTextBox.Text = profile.MaxBackups.ToString();

            // Update button text based on current state
            if (profile.IsAutoBackupEnabled)
            {
                ToggleAutoBackupButton.Content = "Disable Auto-Backup";
            }
            else
            {
                ToggleAutoBackupButton.Content = "Enable Auto-Backup";
            }
        }
    }

    private void ToggleAutoBackupButton_Click(object sender, RoutedEventArgs e)
    {
        var selectedProfile = GameProfilesListBox.SelectedItem as GameProfile;
        if (selectedProfile == null)
        {
            return;
        }

        // Toggle the state
        selectedProfile.IsAutoBackupEnabled = !selectedProfile.IsAutoBackupEnabled;

        // Start or stop auto-backup based on new state
        if (selectedProfile.IsAutoBackupEnabled)
        {
            StartAutoBackup(selectedProfile);
            StatusTextBlock.Text = $"Auto-backup enabled for '{selectedProfile.Name}'";
        }
        else
        {
            StopAutoBackup(selectedProfile);
            StatusTextBlock.Text = $"Auto-backup disabled for '{selectedProfile.Name}'";
        }

        // Save the changes
        SaveProfiles();

        // Update UI to reflect new state
        UpdateAutoBackupControls(selectedProfile);
    }

    private void StartAutoBackup(GameProfile profile)
    {
        if (!profile.IsAutoBackupEnabled)
        {
            return;
        }

        // Stop existing timer if any
        StopAutoBackup(profile);

        // Convert minutes to milliseconds
        int intervalMs = profile.AutoBackupIntervalMinutes * 60 * 1000;

        // Create and start the timer
        var timer = new System.Threading.Timer(
            callback: (state) => PerformAutoBackup(profile),
            state: null,
            dueTime: intervalMs,
            period: intervalMs);

        // Store the timer
        autoBackupTimers[profile.Name] = timer;
    }

    private void StopAutoBackup(GameProfile profile)
    {
        if (autoBackupTimers.TryGetValue(profile.Name, out var timer))
        {
            timer.Dispose();
            autoBackupTimers.Remove(profile.Name);
        }
    }

    private void PerformAutoBackup(GameProfile profile)
    {
        try
        {
            // Validate that SourcePath exists
            if (!System.IO.Directory.Exists(profile.SourcePath))
            {
                return;
            }

            // Define Destination Path
            string timestamp = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
            string backupRootDir = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Backups");
            string gameBackupDir = System.IO.Path.Combine(backupRootDir, profile.Name);
            string destinationPath = System.IO.Path.Combine(gameBackupDir, timestamp);

            // Create directories if they don't exist
            if (!System.IO.Directory.Exists(gameBackupDir))
            {
                System.IO.Directory.CreateDirectory(gameBackupDir);
            }
            System.IO.Directory.CreateDirectory(destinationPath);

            // Execute Backup
            CopyDirectory(profile.SourcePath, destinationPath);

            // Enforce backup limit
            EnforceBackupLimit(profile);

            // Update UI on the main thread
            Dispatcher.Invoke(() =>
            {
                // Check if this game is currently selected
                var selectedProfile = GameProfilesListBox.SelectedItem as GameProfile;
                if (selectedProfile != null && selectedProfile.Name == profile.Name)
                {
                    RefreshBackupsList(selectedProfile);
                }

                StatusTextBlock.Text = $"Auto-backup completed for '{profile.Name}' at {DateTime.Now:HH:mm:ss}";
                PlaySuccessSound();
            });
        }
        catch (Exception ex)
        {
            // Update UI with error message
            Dispatcher.Invoke(() =>
            {
                StatusTextBlock.Text = $"Auto-backup failed for '{profile.Name}': {ex.Message}";
            });
        }
    }

    private void BackupViaHotkey(object? sender, HotkeyEventArgs e)
    {
        var selectedProfile = GameProfilesListBox.SelectedItem as GameProfile;
        if (selectedProfile == null)
        {
            return;
        }

        // Simulate the "Backup Now" button behavior
        BackupSelected_Click(this, new RoutedEventArgs());
        e.Handled = true;
    }

    private void RestoreViaHotkey(object? sender, HotkeyEventArgs e)
    {
        var selectedProfile = GameProfilesListBox.SelectedItem as GameProfile;
        if (selectedProfile == null)
        {
            return;
        }

        // Get the latest backup (first item in the sorted list)
        var backupsList = BackupsListBox.ItemsSource as List<string>;
        if (backupsList == null || backupsList.Count == 0)
        {
            StatusTextBlock.Text = "No backups available to restore.";
            e.Handled = true;
            return;
        }

        string latestBackup = backupsList[0];

        try
        {
            // Construct paths
            string destinationPath = selectedProfile.SourcePath;
            string backupSourcePath = System.IO.Path.Combine(
                AppDomain.CurrentDomain.BaseDirectory,
                "Backups",
                selectedProfile.Name,
                latestBackup);

            // Validate backup source exists
            if (!System.IO.Directory.Exists(backupSourcePath))
            {
                StatusTextBlock.Text = $"Error: Backup directory not found at '{backupSourcePath}'";
                e.Handled = true;
                return;
            }

            // Show confirmation dialog
            string warningMessage = $"WARNING: This will overwrite your current '{selectedProfile.Name}' saves with the backup from '{latestBackup}'.\n\n" +
                                   "This action cannot be undone!\n\n" +
                                   "Are you sure you want to continue?";

            var result = System.Windows.MessageBox.Show(
                warningMessage,
                "Confirm Restore",
                MessageBoxButton.YesNo,
                MessageBoxImage.Warning);

            if (result != MessageBoxResult.Yes)
            {
                StatusTextBlock.Text = "Restore cancelled by user.";
                e.Handled = true;
                return;
            }

            // Clear destination directory
            if (System.IO.Directory.Exists(destinationPath))
            {
                // Delete all files in the destination
                foreach (var file in System.IO.Directory.GetFiles(destinationPath))
                {
                    System.IO.File.Delete(file);
                }

                // Delete all subdirectories in the destination
                foreach (var dir in System.IO.Directory.GetDirectories(destinationPath))
                {
                    System.IO.Directory.Delete(dir, true);
                }
            }
            else
            {
                // Create destination if it doesn't exist
                System.IO.Directory.CreateDirectory(destinationPath);
            }

            // Perform restore
            CopyDirectory(backupSourcePath, destinationPath);

            // Provide feedback
            StatusTextBlock.Text = $"'{selectedProfile.Name}' saves have been successfully restored from '{latestBackup}'.";
        }
        catch (Exception ex)
        {
            StatusTextBlock.Text = $"Restore failed: {ex.Message}";
        }

        e.Handled = true;
    }

    protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
    {
        // Unregister hotkeys
        try
        {
            HotkeyManager.Current.Remove("BackupHotkey");
            HotkeyManager.Current.Remove("RestoreHotkey");
        }
        catch
        {
            // Ignore errors during cleanup
        }

        base.OnClosing(e);
    }

    private void RefreshBackupsList(GameProfile? profile)
    {
        // Clear the backups list
        BackupsListBox.ItemsSource = null;

        // Check if no selection
        if (profile == null)
        {
            return;
        }

        // Construct backup path for the selected game
        string backupFolderPath = System.IO.Path.Combine(
            AppDomain.CurrentDomain.BaseDirectory,
            "Backups",
            profile.Name);

        // Check if the backup folder exists
        if (!System.IO.Directory.Exists(backupFolderPath))
        {
            return;
        }

        // Get all subdirectories (backups)
        var backupDirs = System.IO.Directory.GetDirectories(backupFolderPath);

        // Extract directory names (timestamps)
        var backupTimestamps = backupDirs
            .Select(dir => System.IO.Path.GetFileName(dir))
            .OrderByDescending(name => name)
            .ToList();

        // Display in the BackupsListBox
        BackupsListBox.ItemsSource = backupTimestamps;
    }

    private void SaveProfiles()
    {
        try
        {
            string json = Newtonsoft.Json.JsonConvert.SerializeObject(gameProfiles, Newtonsoft.Json.Formatting.Indented);
            System.IO.File.WriteAllText(ProfilesFilePath, json);
            StatusTextBlock.Text = "Profiles saved successfully.";
        }
        catch (Exception ex)
        {
            StatusTextBlock.Text = $"Failed to save profiles: {ex.Message}";
        }
    }

    private void LoadProfiles()
    {
        if (System.IO.File.Exists(ProfilesFilePath))
        {
            try
            {
                string json = System.IO.File.ReadAllText(ProfilesFilePath);
                if (!string.IsNullOrWhiteSpace(json))
                {
                    var loadedProfiles = Newtonsoft.Json.JsonConvert.DeserializeObject<List<GameProfile>>(json);
                    if (loadedProfiles != null)
                    {
                        gameProfiles = loadedProfiles;
                    }
                }
            }
            catch (Exception ex)
            {
                StatusTextBlock.Text = $"Failed to load profiles: {ex.Message}";
            }
        }
    }

    private void CopyDirectory(string sourceDir, string destDir)
    {
        // Get the source directory info
        System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(sourceDir);

        // Create the destination directory if it doesn't exist
        if (!System.IO.Directory.Exists(destDir))
        {
            System.IO.Directory.CreateDirectory(destDir);
        }

        // Copy all files
        foreach (System.IO.FileInfo file in dir.GetFiles())
        {
            string destFilePath = System.IO.Path.Combine(destDir, file.Name);
            file.CopyTo(destFilePath, true);
        }

        // Recursively copy all subdirectories
        foreach (System.IO.DirectoryInfo subDir in dir.GetDirectories())
        {
            string destSubDirPath = System.IO.Path.Combine(destDir, subDir.Name);
            CopyDirectory(subDir.FullName, destSubDirPath);
        }
    }

    private void EnforceBackupLimit(GameProfile profile)
    {
        // Check if backup limit is enabled (greater than 0)
        if (profile.MaxBackups <= 0)
        {
            return;
        }

        // Construct the path to the game's root backup directory
        string backupRootDir = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Backups");
        string gameBackupDir = System.IO.Path.Combine(backupRootDir, profile.Name);

        // Check if the backup directory exists
        if (!System.IO.Directory.Exists(gameBackupDir))
        {
            return;
        }

        // Get all subdirectories (timestamped backup folders)
        var backupDirs = System.IO.Directory.GetDirectories(gameBackupDir);

        // Sort directories by name in ascending order (oldest first)
        var sortedBackups = backupDirs.OrderBy(dir => System.IO.Path.GetFileName(dir)).ToList();

        // Check if we exceed the limit
        int backupsToDelete = sortedBackups.Count - profile.MaxBackups;
        if (backupsToDelete <= 0)
        {
            return;
        }

        // Delete oldest backups
        for (int i = 0; i < backupsToDelete; i++)
        {
            try
            {
                System.IO.Directory.Delete(sortedBackups[i], true);
            }
            catch (Exception ex)
            {
                // Log error but continue with other deletions
                Dispatcher.Invoke(() =>
                {
                    StatusTextBlock.Text = $"Warning: Failed to delete old backup '{System.IO.Path.GetFileName(sortedBackups[i])}': {ex.Message}";
                });
            }
        }
    }

    private void PlaySuccessSound()
    {
        System.Media.SystemSounds.Asterisk.Play();
    }
}